package com.tianlh.quartz;

import java.util.List;
import java.util.Map;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.core.jmx.JobDataMapSupport;

/**
 * @author 黑子
 * @Date 2018/2/28 15:52
 */
public final class JobHelper {

    private static String DEFAULT_JOB_GROUP_NAME = "DEFAULT_JOB_GROUP";
    private static String DEFAULT_TRIGGER_GROUP_NAME = "DEFAULT_TRIGGER_GROUP";

    public static void addJob(Scheduler scheduler, String jobName, String triggerName, String cron, Class<? extends Job> clazz) {
        addJob(scheduler, jobName, DEFAULT_JOB_GROUP_NAME, triggerName, DEFAULT_TRIGGER_GROUP_NAME, cron, clazz, null);
    }

    public static void addJob(Scheduler scheduler, String jobName, String triggerName, String cron, Class<? extends Job> clazz, Map<String, Object> data) {
        addJob(scheduler, jobName, DEFAULT_JOB_GROUP_NAME, triggerName, DEFAULT_TRIGGER_GROUP_NAME, cron, clazz, data);
    }

    /**
     * 添加一个定时任务，使用默认的任务组名，触发器名，触发器组名
     */
    public static void addJob(Scheduler scheduler, String jobName, String jobGroupName, String triggerName,
                              String triggerGroupName, String cron, Class<? extends Job> clazz, Map<String, Object> data) {
        try {
            scheduler.scheduleJob(newJobDetail(jobName, jobGroupName, clazz, data),
                newTrigger(triggerName, triggerGroupName, cron));
            if (!scheduler.isShutdown()) {
                // The Scheduler cannot be restarted after shutdown() has been called.
                scheduler.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 修改一个任务的触发时间:
     * 通过替换Job实现,删除该job关联的除所有除第一个以外的触发器，并更新此触发器的cron
     */
    public static void modifyJobTime(Scheduler scheduler, String jobName, String cron) {
        modifyJobTime(scheduler, jobName, DEFAULT_JOB_GROUP_NAME, cron);
    }

    /**
     * 修改一个任务的触发时间:
     * 通过替换Job实现,删除该job关联的除所有除第一个以外的触发器，并更新此触发器的cron
     */
    public static void modifyJobTime(Scheduler scheduler, String jobName, String jobGroupName, String cron) {
        try {
            JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobDetail.getKey());
            if (triggers == null && triggers.size() <= 0) {
                return;
            }
            CronTrigger trigger = (CronTrigger)triggers.get(0);
            TriggerKey triggerKey = trigger.getKey();
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(cron)) {
                removeJob(scheduler, jobName, jobGroupName);
                addJob(scheduler, jobName, jobGroupName, triggerKey.getName(), triggerKey.getGroup(), cron,
                    jobDetail.getJobClass(), jobDetail.getJobDataMap());
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static void modifyTriggerTime(Scheduler scheduler, String triggerName, String cron) {
        modifyTriggerTime(scheduler, triggerName, DEFAULT_TRIGGER_GROUP_NAME, cron);
    }

    /**
     * 修改一个触发器的触发时间
     * 通过修改Trigger实现
     */
    public static void modifyTriggerTime(Scheduler scheduler, String triggerName, String triggerGroupName,
                                              String cron) {
        try {
            CronTrigger trigger = (CronTrigger)scheduler.getTrigger(
                TriggerKey.triggerKey(triggerName, triggerGroupName));
            if (trigger == null) {
                return;
            }
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(cron)) {
                TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
                triggerBuilder.withIdentity(triggerName, triggerGroupName);
                triggerBuilder.startNow();
                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
                trigger = (CronTrigger)triggerBuilder.build();
                scheduler.rescheduleJob(TriggerKey.triggerKey(triggerName, triggerGroupName), trigger);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static void removeJob(Scheduler scheduler, String jobName) {
        removeJob(scheduler, jobName, DEFAULT_JOB_GROUP_NAME);
    }

    /**
     * 移除一个任务以及相关所有的触发器
     */
    public static void removeJob(Scheduler scheduler, String jobName, String jobGroupName) {
        try {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(JobKey.jobKey(jobName, jobGroupName));

            for (Trigger trigger : triggers) {
                // 停止触发器
                scheduler.pauseTrigger(trigger.getKey());
                // 移除触发器
                scheduler.unscheduleJob(trigger.getKey());
            }
            // 删除任务
            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static JobDetail newJobDetail(String name, Class<? extends Job> clazz) {
        return newJobDetail(name, DEFAULT_JOB_GROUP_NAME, clazz, null);
    }

    public static JobDetail newJobDetail(String name, Class<? extends Job> clazz, Map<String, Object> data) {
        return newJobDetail(name, DEFAULT_JOB_GROUP_NAME, clazz, data);
    }

    public static JobDetail newJobDetail(String name, String groupName, Class<? extends Job> clazz, Map<String, Object> data) {
        JobDetail jobDetail = JobBuilder.newJob(clazz).
            withIdentity(name, groupName).build();
        if(data != null){
            return jobDetail.getJobBuilder().setJobData(JobDataMapSupport.newJobDataMap(data)).build();
        }
        return jobDetail;
    }


    /*
    * 1. Seconds 秒
    * 2. Minutes 分钟
    * 3. Hours 小时
    * 4. Day-of-Month 月中的天
    * 5. Month 月
    * 6. Day-of-Week 周中的天
    * 7. Year (optional field) 年（可选的域）
    * 通配符（'*'）可以被用来表示域中"每个"可能的值
    * 秒和分域的合法值为 0 到 59，小时的合法范围是 0 到 23，Day-of-Month 中值得合法凡范围是 0到 31，但是需要注意不同的月份中的天数不同。
    * 月份的合法值是 0 到 11。或者用字符串JAN,FEB MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV 及 DEC 来表示。Days-of-Week 可以用 1
    * 到 7 来表示（1=星期日）或者用字符串 SUN, MON, TUE, WED,THU, FRI 和 SAT 来表示。
    * '/'字符用来表示值的增量，例如, 如果分钟域中放入'0/15'，它表示"每隔 15 分钟，从 0 开始"
    * '?'字符可以用在 day-of-month 及 day-of-week 域中，它用来表示“没有指定值”。
    * 'L'字符可以在 day-of-month 及 day-of-week 中使用，这个字符是"last"的简写，但是在两个域中的意义不同。
    * 例如，在 day-of-month 域中的"L"表示这个月的最后一天，如果它用在 day-of-week 中，则表示"7"或者"SAT"。但是如果在 day-of-week 域中，
    * 这个字符跟在别的值后面，则表示"当月的最后的周 XXX"。例如："6L" 或者 "FRIL"都表示本月的最后一个周五。
    * '#'表示表示月中的第几个周几。例如：day-of-week 域中的"6#3" 或者 "FRI#3"表示"月中第三个周五"。
    *
    * 例子：
    * 在每分钟的10秒后每隔5分钟触发一次的表达式(例如. 10:00:10am, 10:05:10 等.)。
    * 在每个周三和周五的 10：30，11：30，12：30 触发的表达式。"0 30 10-13 ? * WED,FRI"
    * 在每个月的 5 号，20 号的 8 点和 10 点之间每隔半个小时触发一次且不包括 10 点，只是 8：30，9：00 和 9：30 的表达式。"0 0/30 8-9 5,20 * ?"
    *
     */

    public static Trigger newTrigger(String name, String groupName, String cron) {
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name,
            groupName).withSchedule(scheduleBuilder).build();
        return trigger;
    }

    public static Trigger newTrigger(String name, String cron) {
        return newTrigger(name, DEFAULT_TRIGGER_GROUP_NAME, cron);
    }
}
